#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <sqlite3.h>
#import <UIKit/UIApplication.h>
#import <SpringBoard/SBApplicationController.h>
#import <SpringBoard/SBApplication.h>
#import <LockInfo/Plugin.h>

#import "framework/FWCommon.h"
#import "framework/FWApplication.h"
#import "framework/FWDBAccess.h"

#import "PIPlugin.h"
#import "PIIntegration.h"


NSString* const PIIntegrationContextPasteboard = @"net.webis.pocketinformant.contextLocations";
NSString* const PIContextFieldNameTitle = @"title"; 
NSString* const PIContextFieldNameKey = @"pk"; 

static NSString* const kAttrColorTop = @"ColTop";
static NSString* const kAttrColorHigh = @"ColHigh";
static NSString* const kAttrColorNormal = @"ColNorm";
static NSString* const kAttrColorLow = @"ColLow";
static NSString* const kAttrColorNone = @"ColNone";
static NSString* const kAttrColorOverdue = @"ColOver";

static NSString* const kAttrColorBGTop = @"ColBGTop";
static NSString* const kAttrColorBGHigh = @"ColBGHigh";
static NSString* const kAttrColorBGNormal = @"ColBGNorm";
static NSString* const kAttrColorBGLow = @"ColBGLow";
static NSString* const kAttrColorBGNone = @"ColBGNone";

static NSString* const kPILocationsEnabled = @"PISignificantLocationChangeMonitoring";


@implementation PIIntegration


// ******************************************************************************************
#pragma mark -
#pragma mark Colors from Pocket Informant

static UIColor* PIColorFromPrefs(NSDictionary* prefs, NSString* key, double defRed, double defGreen, double defBlue)
{
    if (NSArray* color = [prefs valueForKey:key])
        return [UIColor colorWithRed:[[color objectAtIndex:0] doubleValue] 
                               green:[[color objectAtIndex:1] doubleValue]
                                blue:[[color objectAtIndex:2] doubleValue]
                               alpha:[[color objectAtIndex:3] doubleValue]];
    else
        return [UIColor colorWithRed:defRed
                               green:defGreen
                                blue:defBlue
                               alpha:1.0];	
}

+ (void)updateColorsFromPrefs:(NSDictionary*)prefs application:(FWApplication*)application 
{
    [application setAttribute:kAttrColorNone value:PIColorFromPrefs(prefs, @"PICalPriorityColor_0_text", 0, 0, 0)];
    [application setAttribute:kAttrColorTop value:PIColorFromPrefs(prefs, @"PICalPriorityColor_1_text", 1, 1, 1)];
    [application setAttribute:kAttrColorHigh value:PIColorFromPrefs(prefs, @"PICalPriorityColor_5_text", 1, 1, 1)];
    [application setAttribute:kAttrColorNormal value:PIColorFromPrefs(prefs, @"PICalPriorityColor_10_text", 0, 0, 0)];
    [application setAttribute:kAttrColorLow value:PIColorFromPrefs(prefs, @"PICalPriorityColor_15_text", 0, 0, 0)];

    [application setAttribute:kAttrColorBGNone value:PIColorFromPrefs(prefs, @"PICalPriorityColor_0_background", 0.247058823529412, 0.247058823529412, 0.247058823529412)];
    [application setAttribute:kAttrColorBGTop value:PIColorFromPrefs(prefs, @"PICalPriorityColor_1_background", 1, 0, 0)];
    [application setAttribute:kAttrColorBGHigh value:PIColorFromPrefs(prefs, @"PICalPriorityColor_5_background", 1, 0.4980392156862745, 0)];
    [application setAttribute:kAttrColorBGNormal value:PIColorFromPrefs(prefs, @"PICalPriorityColor_10_background", 0, 1, 0)];
    [application setAttribute:kAttrColorBGLow value:PIColorFromPrefs(prefs, @"PICalPriorityColor_15_background", 0, 1, 1)];
    
    [application setAttribute:kAttrColorOverdue value:PIColorFromPrefs(prefs, @"PIColorTaskOverdue", 1, 0.4470588235294118, 0.4470588235294118)];
}

+ (UIColor*)colorForOverdue:(FWApplication*)application 
{
    return (UIColor*)[application getAttribute:kAttrColorOverdue];
}    

+ (UIColor*)colorForAction:(int)action application:(FWApplication*)application 
{
    if (action <= PIActionNone)
        return (UIColor*)[application getAttribute:kAttrColorNone];
    else if (action <= PIActionPlanning)
        return (UIColor*)[application getAttribute:kAttrColorHigh];
    else if (action <= PIActionSomeday)
        return (UIColor*)[application getAttribute:kAttrColorNormal];
    else
        return (UIColor*)[application getAttribute:kAttrColorLow];
}

+ (UIColor*)colorBGForAction:(int)action application:(FWApplication*)application 
{
    if (action <= PIActionNone)
        return (UIColor*)[application getAttribute:kAttrColorBGNone];
    else if (action <= PIActionPlanning)
        return (UIColor*)[application getAttribute:kAttrColorBGHigh];
    else if (action <= PIActionSomeday)
        return (UIColor*)[application getAttribute:kAttrColorBGNormal];
    else
        return (UIColor*)[application getAttribute:kAttrColorBGLow];
}

+ (UIColor*)colorForImportance:(int)importance application:(FWApplication*)application 
{
    if (importance <= PIImportanceNone)
//        return [UIColor grayColor];
        return (UIColor*)[application getAttribute:kAttrColorNone];
    else if (importance <= PIImportanceTop)
        return (UIColor*)[application getAttribute:kAttrColorTop];
    else if (importance <= PIImportanceHigh)
        return (UIColor*)[application getAttribute:kAttrColorHigh];
    else if (importance <= PIImportanceNormal)
        return (UIColor*)[application getAttribute:kAttrColorNormal];
    else
        return (UIColor*)[application getAttribute:kAttrColorLow];
}

+ (UIColor*)colorBGForImportance:(int)importance application:(FWApplication*)application 
{
    if (importance <= PIImportanceNone)
//        return [UIColor grayColor];
        return (UIColor*)[application getAttribute:kAttrColorBGNone];
    else if (importance <= PIImportanceTop)
        return (UIColor*)[application getAttribute:kAttrColorBGTop];
    else if (importance <= PIImportanceHigh)
        return (UIColor*)[application getAttribute:kAttrColorBGHigh];
    else if (importance <= PIImportanceNormal)
        return (UIColor*)[application getAttribute:kAttrColorBGNormal];
    else
        return (UIColor*)[application getAttribute:kAttrColorBGLow];
}


// ******************************************************************************************
#pragma mark -
#pragma mark Date management using Pocket Informant conventions

static BOOL isYesterday(NSDate* date) 
{
    return isSameDay(date, [NSDate dateWithTimeIntervalSinceNow:-SECONDS_PER_DAY]);
}

+ (NSDate*)utcDateForLocalMidnight
{
    // Get time of midnight today (ie past) in the UTC by building a new date from only the year, month
    // and day components of the current date. PI start dates are always UTC
    NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
    gregorian.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
    NSDateComponents *components = [gregorian components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
                                                fromDate:[NSDate date]];
    NSDate *midnight = [gregorian dateFromComponents:components];
    
    return midnight;
}

+ (NSDate*)localDateForDateNumber:(NSNumber*)dateNum
{
    double date = dateNum.doubleValue;
    
    // PI stores dates as relative to UTC
    NSDate* utcDate = [NSDate dateWithTimeIntervalSinceReferenceDate:date];
    
    // Get seconds from UTC for the required date
    NSInteger localOffset = [[NSTimeZone systemTimeZone] secondsFromGMTForDate:utcDate];
    
    return [NSDate dateWithTimeIntervalSinceReferenceDate:(date - localOffset)];
}

+ (NSDate*)localDateForTimeNumber:(NSNumber*)dateNum
{
    // PI stores times as local to the current TZ
    return [NSDate dateWithTimeIntervalSinceReferenceDate:dateNum.doubleValue];
}

+ (NSString*)dateStringForDateNumber:(NSNumber*)dateNum withTime:(BOOL)time format:(DateFormatterFormat)format
{
    NSBundle* bundle = [NSBundle bundleForClass:[self class]];
    NSString* result = @"\u2205"; // Empty set symbol
    
    if (dateNum.doubleValue != 0.0) {
        // Required time in UTC
        NSDate* localDate = time ? [PIIntegration localDateForTimeNumber:dateNum] : [PIIntegration localDateForDateNumber:dateNum]; 
        
        NSDateFormatter* df = [[NSDateFormatter alloc] init];
        if (format == DateFormatterLongFormat) {
            // Use long date format - eg "November 23, 1937"
            df.dateStyle = NSDateFormatterLongStyle;
            df.timeStyle = NSDateFormatterNoStyle;
        }
        else if (format == DateFormatterCompressedFormat) {
            // Use shortest date format - eg "11/23/2010"
            df.dateStyle = NSDateFormatterShortStyle;
            df.timeStyle = NSDateFormatterNoStyle;
            
            // Change 4 digit year to 2 to make shorted - eg "11/23/10"
            df.dateFormat = [df.dateFormat stringByReplacingOccurrencesOfString:@"yyyy" withString:@"yy"];
        }
        else
            // Use standard date format - eg "Mon 23 Jun"
            df.dateFormat = (NSString*)UIDateFormatStringForFormatType(CFSTR("UIAbbreviatedWeekdayMonthDayFormat"));
        
        if (format == DateFormatterCompressedFormat) {
            result = [df stringFromDate:localDate];
        }
        else {
            // Use relative dates if suitable
            if (isToday(localDate))
                result = localize(bundle, @"Date/Today");
            else if (isTomorrow(localDate))
                result = localize(bundle, @"Date/Tomorrow");
            else if (isYesterday(localDate))
                result = localize(bundle, @"Date/Yesterday");
            else {
                result = [df stringFromDate:localDate];
                
                // If using long format then append the day name, eg "Mon, November 23, 1937"
                if (format == DateFormatterLongFormat) {
                    NSString* replace = localize(bundle, @"Date/Long Format");
                    df.dateFormat = @"ccc";
                    replace = [replace stringByReplacingOccurrencesOfString:@"%DAY%" withString:[df stringFromDate:localDate]];
                    result = [replace stringByReplacingOccurrencesOfString:@"%DATE%" withString:result];
                }
            }
        }
        
        // Add the time if provided
        if (time) {
            NSDateFormatter* tf = [[NSDateFormatter alloc] init];
            tf.dateStyle = NSDateFormatterNoStyle;
            tf.timeStyle = NSDateFormatterShortStyle;
            
            result = [NSString stringWithFormat:@"%@ %@", result, [tf stringFromDate:localDate]];
            
            [tf release];
        }
        
        //DebugLog(@"LI:PITodo: formatting time num=%F Local=%@ Time=%@", dateNum.doubleValue, localDate, result);
        
        [df release];
    }
    
    return result;
}


// ******************************************************************************************
#pragma mark -
#pragma mark Internal names from Pocket Informant values

+(NSString*) nameForAction:(int)action
{
    switch (action) {
        case PIActionNext:
            return @"Action/Next Action";
        case PIActionActive:
            return @"Action/Active";
        case PIActionPlanning:
            return @"Action/Planning";
        case PIActionDelegated:
            return @"Action/Delegated";
        case PIActionWaiting:
            return @"Action/Waiting";
        case PIActionHold:
            return @"Action/Hold";
        case PIActionPostponed:
            return @"Action/Postponed";
        case PIActionSomeday:
            return @"Action/Someday";
        case PIActionCancelled:
            return @"Action/Cancelled";
        case PIActionReference:
            return @"Action/Reference";
        default:
            return @"Action/None";
    }
}

+(NSString*) nameForImportance:(int)importance
{
    switch (importance) {
        case PIImportanceTop:
            return @"Importance/Top";
        case PIImportanceHigh:
            return @"Importance/High";
        case PIImportanceNormal:
            return @"Importance/Normal";
        case PIImportanceLow:
            return @"Importance/Low";
        default:
            return @"Importance/None";
    }
}


// ******************************************************************************************
#pragma mark -
#pragma mark Paths to items within the Pocket Informant application

+ (NSString*)iconFileForAction:(int)action
{
    NSString* actionIcon = nil;
    
    switch (action) {
        case PIActionNext:
            actionIcon = @"NextAction";
            break;
        case PIActionActive:
            actionIcon = @"Active";
            break;
        case PIActionPlanning:
            actionIcon = @"Planning";
            break;
        case PIActionDelegated:
            actionIcon = @"Delegated";
            break;
        case PIActionWaiting:
            actionIcon = @"Waiting";
            break;
        case PIActionHold:
            actionIcon = @"Hold";
            break;
        case PIActionPostponed:
            actionIcon = @"Postponed";
            break;
        case PIActionSomeday:
            actionIcon = @"Someday";
            break;
        case PIActionCancelled:
            actionIcon = @"Cancelled";
            break;
        case PIActionReference:
            actionIcon = @"Reference";
            break;
        default:
            actionIcon = nil;
            break;
    }
    
    return actionIcon;
}

+ (NSString*)iconPathForAction:(int)action application:(FWApplication*)application 
{
    NSString* icon = [PIIntegration iconFileForAction:action];
    if (icon)
        return [application.applicationPath stringByAppendingFormat:@"/%@.png", icon];
    else
        return nil;
}

+ (NSString*)iconPathForSmallFlag:(FWApplication*)application
{
    return [application.applicationPath stringByAppendingString:@"/star_yellow_small.png"];
}

+ (NSString*)iconPathForLargeFlag:(FWApplication*)application
{
    return [application.applicationPath stringByAppendingString:@"/star_yellow.png"];
}

+ (NSString*)iconPathForRepeat:(FWApplication*)application
{
    return [application.applicationPath stringByAppendingString:@"/recurrence_icon.png"];
}

+ (NSString*)iconPathForParent:(FWApplication*)application
{
    return [application.applicationPath stringByAppendingString:@"/tree_parent_icon.png"];
}

+ (NSString*)iconPathForChild:(FWApplication*)application
{
    return [application.applicationPath stringByAppendingString:@"/tree_icon.png"];
}


// ******************************************************************************************
#pragma mark -
#pragma mark Location-based context handling

+ (BOOL)locationMonitoringForPrefs:(NSDictionary*)prefs 
{
    // Unless we know explicitly that location monitoring is off then assume it's on
    if (NSNumber* enabled = [prefs objectForKey:kPILocationsEnabled])
        return [enabled boolValue];
    else
        return YES;
}

+ (NSArray*)locationContextsFromPI 
{
    UIPasteboard* pasteBoard = [UIPasteboard pasteboardWithName:PIIntegrationContextPasteboard create:NO];
    NSMutableArray* result = [NSMutableArray arrayWithCapacity:10];
    
    if (pasteBoard) {
        NSArray* items = [pasteBoard items];
        for (NSDictionary* item in items) {
            NSData* pkData = [item objectForKey:@"pk"];
            NSData* pkTitle = [item objectForKey:@"title"];
            
            NSString* title = [[[NSString alloc] initWithData:pkTitle encoding:NSASCIIStringEncoding] autorelease];
            NSNumber* pk = [NSPropertyListSerialization propertyListWithData:pkData options:0 format:NULL error:NULL];
            
            // Do we already have this value? Sometimes PI sends the same one twice!
            BOOL exists = NO;
            for (NSDictionary* test in result) {
                // If we already have this value then skip it and move to the next
                if ([[test objectForKey:PIContextFieldNameKey] intValue] == [pk intValue]) {
                    exists = YES;
                    break;
                }
            }
            
            if (!exists)
                [result addObject:[NSDictionary dictionaryWithObjectsAndKeys:
                                   title, PIContextFieldNameTitle,
                                   pk, PIContextFieldNameKey,
                                   nil]];
        }
    }

    NSLog(@"LI:PITodo: Updated location contexts from PI");
    DebugLog(@"LI:PITodo: New contexts = %@", result);

    return result;
}

+ (NSString*)newTaskURL:(NSString*)title importance:(enum PIImportanceType)importance application:(FWApplication*)app
{
    NSString* encodedTitle = FWXMLEncodeString(title);
    NSString* encodedCalendar = FWXMLEncodeString(@"Internal Task Calendar");

    // Get the highest current task id
    PIIntegration* obj = [[PIIntegration alloc] init];
    NSNumber* maxKey = (NSNumber*)[FWDBGetRecords(@"select max(id) from task", 1, app.databasePath, app, obj) objectAtIndex:0];
    [obj release];
    
    NSDateFormatter *format = [[NSDateFormatter alloc] init];
    [format setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
    [format setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'Z'"];
    NSString* currentDate = [format stringFromDate:[NSDate date]]; 
    [format release];
    
    NSString* importanceCode;
    switch (importance) {
        case PIImportanceTop:
            importanceCode = @"3";//none
            break;
        case PIImportanceHigh:
            importanceCode = @"2";//ok
            break;
        case PIImportanceNormal:
            importanceCode = @"1";//med
            break;
        case PIImportanceLow:
            importanceCode = @"0";//low
            break;
        default:
            importanceCode = @""; //low
            break;
    }
    
    NSString* params = [NSString stringWithFormat:@"<data>"
                        "<task id=\"%i\">"
                        "<title>%@</title>"
                        "<modified>%@</modified>"
                        "<startdate/><duedate/>"
                        "<teamtask>false</teamtask>"
                        "<completed>NO</completed>"
                        "<percentcomplete>0</percentcomplete>"
                        "<priority>%@</priority>"
                        "<priorityandrank/>"
                        "<sensitivity>0</sensitivity>"
                        "<status>0</status>"
                        "<note></note>"
                        "<color>#000000</color>"
                        "<categories>%@</categories>"
                        "<repeat>0</repeat>"
                        "<recurrence><data></data></recurrence>"
                        "</task>"
                        "</data>",
                        [maxKey intValue] + 1,
                        encodedTitle,
                        currentDate,
                        importanceCode,
                        encodedCalendar];

    NSString* url = [NSString stringWithFormat:@"informant://createTask?%@",
                     FWBase64EncodeString(params)];
    
    return url;
}

- (NSObject*)objectForSQLRecord:(sqlite3_stmt *)sqlStatement withId:(NSInteger)sqlid application:(FWApplication *)application
{
    return [NSNumber numberWithInt:sqlite3_column_int(sqlStatement, 0)];
}

@end
